//#include "EstruturaDinamica.h"
#include "Constantes_Globals.h"

/**
 * Implementacao de Estrutra de Dados para Escalonamento
 * ---------------------------------------------------------
 *
 * Importantes funcoes de Lista e Fila Dinamica Duplamente ligada
 * com o anterior e o proximo.
 *
 * Como sera necessario inserir e retirar elementos da extremidades
 * opostas da lista, teremos dois ponteiros ini e fim, que apontam
 * para o primeiro e fim da estrutura.
 *
 * O algoritmo ed_fifo(f) tem como a funcao de fazer os apontamentos
 * dos processos.
 *
 * @author Caio Thomas Oliveira 11011BSI209
 * @author Leonardo Pimentel Ferreira 11011BSI223
 * @version 01.07.2012
 */

/**
 * Funcao que inicializa a estrutura de uma lista
 * @return ponteiro da estrutura com inicio e fim da lista
 */
Estrutura*
ed_nova()
{
    Estrutura *e = (Estrutura *) malloc(sizeof(Estrutura));

    e->ini=NULL;
    e->fim=NULL;
    e->tamanho= (int) 0;
    return e;
}

/**
 * Funcao que insere um elemento no inicio da lista
 * @param ponteiro para estrutura *f
 * @param processo
 * @return void
 */
void
ed_insere_inicio(Estrutura *f, Processo *processo)
{
    Lista *no = (Lista*) malloc(sizeof(Lista));
    no->processo = processo;
    no->prox = f->ini;
    no->ant = NULL;

    //verifica se a lista nao esta vazia
    if(f->ini != NULL)
        f->ini->ant = no;//o f->ini aponta anteriormente para inicio para novo no
    
    f->ini = no;//e atualiza o inicio sendo o novo no

    if(f->fim==NULL)
        f->fim = f->ini;        
    
    f->tamanho++;
}


/**
 * Funcao que insere um elemento no fim da lista
 * @param ponteiro para estrutura *f
 * @param processo
 * @return void
 */
void
ed_insere_fim(Estrutura *f, Processo *processo)
{    
    Lista *no = (Lista*) malloc(sizeof(Lista));
    no->processo = processo;
    no->prox = NULL;
    no->ant = NULL;
    
    if(f->tamanho == 0)
    {
        f->ini = f->fim = no;
        f->tamanho = 1;
        return;
    }
    
    no->ant = f->fim;
    f->fim->prox = no;
    f->fim = no;
    f->tamanho++;
}

/**
 * Desloca o elemento do inicio para o fim da estrutura
 *  - ini aponta para o proximo
 *  - fim aponta para o processo que acabou de sai do inicio
 * @param lista *f
 * @return void
 */
void
ed_fifo(Estrutura *f)
{   
    if(ed_vazia(f) || f->tamanho == 1)
    {        
        return;
    }
          
    ed_insere_fim(f, f->ini->processo);
    ed_remove_inicio(f, NULL);
    
}

/**
 * Funcao que verifica se a lista eh vazia
 * @param ponteiro para Estrutura
 * @return inteiro 1 erro ou 0 sucesso
 */
int
ed_vazia(Estrutura *f)
{
    return (f->ini==NULL);
}

/**
 * Imprime o conteudo da lista
 * @param ponteiro para Estrutura
 */
void
ed_imprime(Estrutura* f)
{  
    if(ed_vazia(f))
    {
        fprintf(log_file,"\nLista de Estrutura vazia - Impossivel imprimir!\n");
        return;
    }

    fprintf(log_file,"\nImpressao da Estrutura da Lista\n");
    
    Lista* q = f->ini;                                
    while(q != NULL)
    {
        fprintf(log_file,"%d \n", q->processo->pid);
        q = q->prox;
    }
}

/**
 * Funcao que imprime a prioridade da Lista
 * @param ponteiro da estrutura f
 * @return void
 */
void
ed_imprime_prioridade(Estrutura* f){
    if(ed_vazia(f))
    {
        fprintf(log_file,"\nLista de Estrutura vazia - Impossivel imprimir!\n");
        return;
    }

    fprintf(log_file,"\nImpressao da Estrutura da Lista\n");
    
    Lista* q = f->ini;
    while(q != NULL)
    {
        fprintf(log_file,"PID %d  --  PRIORIDADE %d\n", q->processo->pid, q->processo->prioridade);
        q = q->prox;
    }
}

/**
 * Funcao que remove todos os nos e estrutura da lista
 * @param estrutura e
 * @return void
 */
Estrutura*
ed_free(Estrutura *e)
{
    Lista *aux = e->ini;
    while(aux != NULL)//libera os nos da lista
    {
        Lista *t = aux->prox;
        free(aux);
        aux = t;
    }
    /* a estrutura de inicio fica como null, pois nao
       nao eh desejavel excluir ela */
    e->ini=NULL;
    e->fim=NULL;
    return e;
}

/**
 * Funcao que remove o primeiro elemento da Estrutura
 * @param Estrutura f
 * @return Processo removido
 */
Processo*
ed_remove_inicio(Estrutura *f, Processo *p)
{
    if(ed_vazia(f))
    {
        fprintf(log_file,"\nLista de Estrutura vazia - Impossivel remover!\n");
        return;
    }    

    Lista *aux;       

    aux = f->ini;//lista aux recebe o primeiro elemento    
    p = aux->processo;//p recebe o processo do elmento que esta removendo

    f->ini = aux->prox;//f inicio recebe o proximo do aux

    if(f->ini != NULL)//anterior vale null
        f->ini->ant = NULL;
    else
        f->fim = NULL;
    
    if(f->tamanho > 0)
        f->tamanho--;
    
    free(aux); //libera memoria
   
    return p;//retorna processo removido
}

/**
 * Funcao que remove o último elemento da Estrutura
 * @param Estrutura ed
 * @return Processo removido
 */

Processo*
ed_remove_fim(Estrutura *ed, Processo *p)
{
    if(ed_vazia(ed))
    {
        fprintf(log_file,"\nLista de Estrutura vazia - Impossivel remover!");
        return;
    }   

    Lista *aux;

    aux = ed->fim;//aux aponta para endereco do fim da lista
    p = aux->processo;//processo eh armazendo para retorno    
    ed->fim = aux->ant;//ed fim recebe aux   

    //verifica se a lista nao ficou vazia
    if(ed->fim != NULL)
        ed->fim->prox = NULL;//axu prox eh null
    else
        ed->ini = NULL;
    
    if(ed->tamanho > 0)
        ed->tamanho--;
    
    free(aux); //elimino o ultimo elemento*/
    return p;//retorna processo removido
}

/**
 * Funcao que retorna um processo correspondente ao pid passado como parâmetro
 * @param Estrutura ed
 * @param int pid
 * @return Processo correspondente ao pid
 */

Processo*
ed_encontra_processo_por_pid(Estrutura *ed, int pid){
    Lista* q = ed->ini;
    while(q != NULL){        
        if(q->processo->pid == pid){
            return (q->processo);
        }
        q = q->prox;
    }
    return NULL;
}

/**
 * Funcao que retorna um nó de acordo com o pid passado como parâmetro
 * @param Estrutura f
 * @param int pid 
 * @return retorna elemento no
 */
Lista*
ed_encontra_no_por_pid(Estrutura *ed, int pid){
    Lista* q = ed->ini;
    while(q != NULL){        
        if(q->processo->pid == pid){
            return q;
        }
        q = q->prox;
    }
    return NULL;
}

/**
 * Funcao que remove um processo em qualquer posição da estrutura
 * @param Estrutura ed
 * @param Processo processo
 * @return Processo removido
 */
Processo*
ed_remove_meio(Estrutura* ed, Processo *processo){    
    if(ed->tamanho > 0){                        
        if(ed->ini->processo->pid == processo->pid){//se o processo está no início da estrutura              
            return ed_remove_inicio(ed, processo);
        }else{
            if(ed->fim->processo->pid == processo->pid){//se o processo está no fim da estrutura                            
                return ed_remove_fim(ed, processo);
            }else{
                Lista* aux = ed_encontra_no_por_pid(ed, processo->pid);                
                aux->ant->prox = aux->prox;
                aux->prox->ant = aux->ant;
                free(aux);
                ed->tamanho--;                
            }
        }
    }    
    return processo;       
}

/**
 * Funcao que reposiciona um nó na 1ª posição da estrutura
 * @param Estrutura ed
 * @param Lista no
 */
void
ed_altera_posicao_first(Estrutura* ed, Lista* no){
    if(ed->tamanho > 1 && ed->ini != no){//se existem pelo menos 2 nodes e o nó que será colocado na 1ª posição ainda não está na 1ª posição
        
        Lista* ant = no->ant;        
        Lista* pos = no->prox;
        
        ant->prox = pos;
        
        if(pos != NULL){
            pos->ant = ant;
        }else{
            ed->fim = ant;
        }
        no->ant = NULL;
        no->prox = ed->ini;
        ed->ini = no;        
    }
}